from datetime import datetime
from typing import List
from databases import Database
from fastapi import FastAPI
from fastapi.routing import APIRoute
from sqlalchemy import create_engine, delete, insert, select

from forward.utils.tool import set_to_str
from .schema import RouteInSchema, RouteOutSchema
from .model import RouteModel, BaseModel
from forward.extend.databases import db


class SyncRoutes():
    def __init__(self) -> None:
        self.app = None

    async def init_app(self, app: FastAPI):
        '''初始化权限'''
        self.app = app
        print("同步路由")
        # print("app routes", app.routes)
        await self.sync_routes()

    async def sync_routes(self):
        '''同步路由'''
        # 创建数据表
        await self.create_route_model()
        # 获取所有路由
        routes_running = self.get_routes_running()
        # 获取数据库路由
        routes_db = await self.get_routes_db()
        # 对比路由
        routes_add, routes_db_deleteIds = await self.constrast(routes_running, routes_db)
        # 同步路由
        await self.sync_db(routes_db_deleteIds, routes_add)

    async def check_database_connection(self, database_url: str) -> bool:
        """检测数据库连接是否正常"""
        try:
            async with Database(database_url) as database:
                # 尝试执行一个简单的查询
                query = "SELECT 1"
                result = await database.fetch_one(query)
                if result and result[0] == 1:
                    return True
        except Exception as e:
            print(f"数据库连接失败: {e}")
        return False

    async def create_route_model(self, table_name: str = "route"):
        # 检测数据库连接
        from forward.config.settings import get_settings
        database_url = get_settings().database_url
        # print("创建数据表 databases url:", database_url)
        # print("route table", RouteModel.__table__.c.path==RouteModel.path)
        # print(dir(RouteModel.__table__))
        if not await self.check_database_connection(database_url):
            raise Exception("数据库连接失败")

        # 检测数据库是否存在route
        try:
            await db.execute(f"SELECT * FROM {table_name}")
        except Exception as e:
            print(f"数据库表不存在，创建表: {table_name}")
           # 创建route数据表
            BaseModel.metadata.create_all(bind=create_engine(database_url))

    def get_routes_running(self):
        '''同步所有路由信息'''
        routes = []
        if self.app is not None:
            for route in self.app.routes:
                route_dict = {}
                # print("route running", route.path)
                # self.routes.append(route)
                '''
                [Route(path='/openapi.json', name='openapi', methods=['GET', 'HEAD']),
                Route(path='/docs', name='swagger_ui_html',
                    methods=['GET', 'HEAD']),
                Route(path='/docs/oauth2-redirect',
                    name='swagger_ui_redirect', methods=['GET', 'HEAD']),
                Route(path='/redoc', name='redoc_html',
                      methods=['GET', 'HEAD']),
                APIRoute(path='/admin/user/', name='list', methods=['GET']),
                APIRoute(path='/admin/user/', name='create', methods=['POST']),
                APIRoute(path='/admin/user/test', name='test', methods=['GET'])]
                '''
                if isinstance(route, APIRoute):
                    # 获取视图函数
                    route_dict = {
                        "endpoint": route.endpoint.__name__,
                        "path": route.path,
                        "methods": set_to_str(route.methods),
                        "tags":  ",".join(map(str, route.tags)) if isinstance(route.tags, list) else "",
                        "summary": route.summary,
                        "update_time": datetime.now(),
                        "permission": self.machine_permission(route.path, route.endpoint.__name__),
                        "description": route.description,
                    }
                    routes.append(RouteInSchema.model_validate(route_dict))
        # print(routes)
        return routes

    async def get_routes_db(self) -> List[RouteOutSchema]:
        '''获取所有路由'''
        results = await db.fetch_all(select(RouteModel))
        # print("routes_db", results)
        return [RouteOutSchema.model_validate(
            result, from_attributes=True) for result in results]

    def machine_permission(self, path: str, endpoint: str):
        parts = path.strip('/').split('/')
        permission = ""
        for part in parts:
            permission = permission+":"+part
        permission = permission.replace(":", "", 1)
        return permission

    async def constrast(self, routes_running: List[RouteInSchema], routes_db: List[RouteOutSchema]):
        '''对比路由'''
        routes_db_deleteIds = [route.id for route in routes_db]
        routes_add = []
        is_exist = False
        for route_running in routes_running:
            # print("route_running", type(route_running))
            for route_db in routes_db:
                if (
                    route_running.path == route_db.path
                    and route_running.endpoint == route_db.endpoint
                    and route_running.summary == route_db.summary
                    and route_running.tags == route_db.tags
                    and route_running.methods == route_db.methods
                    and route_running.permission == route_db.permission
                ):
                    # 更新路由
                    if route_db.id in routes_db_deleteIds:
                        routes_db_deleteIds.remove(route_db.id)
                        is_exist = True
                        break
                else:
                    # 新增路由
                    is_exist = False
            if not is_exist:
                routes_add.append(route_running)
        return routes_add, routes_db_deleteIds

    async def sync_db(self, routes_db_deleteIds: List[int], routes_add: List[RouteInSchema]):
        """同步路由"""
        # print("route_add", routes_add)
        # 删除路由
        await db.execute(delete(RouteModel).where(RouteModel.id.in_(routes_db_deleteIds)))
        # 新增路由
        if routes_add:
            await db.execute(insert(RouteModel).values([route_add.model_dump() for route_add in routes_add]))
